#include "stdafx.h"
#include "GameObject.h"

#include "..\Common\CommonUtil.h"

#include "ClientObjectManager.h"
#include "ClientServiceRequester.h"
#include "ClientThreads.h"


static CMesh* gs_MissileMesh = NULL;
static CMesh* gs_BoomMesh = NULL;
static CMesh* gs_ShipMesh = NULL;

const float MISSILE_SPEED = 1300.0f;


object_manager::object_manager(void) 
: m_pClient(NULL), m_pServiceRequester(NULL), m_pCommander(NULL), m_UdpNetwork(NULL), m_ShipCode(0) {
	m_ShowRecvSyncDataCount			= false;
	m_ShowEffectiveRecvSyncDataCount= false;
	m_ShowMySendSyncDataCount		= false;
	m_LastMySendSyncDataCount		= 0;
	m_CurrentMySendSyncDataCount	= 0;
	m_LastUpdateMySendSyncDataTick	= 0;
	m_PosInterpolation				= true;
}
object_manager::~object_manager(void) {	
	delete m_UdpNetwork;

	if(gs_MissileMesh != NULL) {
		delete gs_MissileMesh;
		gs_MissileMesh = NULL;
	}
	if(gs_BoomMesh != NULL) {
		delete gs_BoomMesh;
		gs_BoomMesh = NULL;
	}
	if(gs_ShipMesh != NULL) {
		delete gs_ShipMesh;
		gs_ShipMesh = NULL;
	}
}

void object_manager::LinkClient(client_base* pClient) {
	assert(m_pClient == NULL);
	assert(pClient != NULL);

	m_pClient = pClient;
}

void object_manager::LinkServiceRequester(service_requester* pServiceRequester) {
	assert(m_pServiceRequester == NULL);
	assert(pServiceRequester != NULL);

	m_pServiceRequester = pServiceRequester;
}

void object_manager::LinkCommander(hz::commander* pCommander) {
	assert(m_pCommander == NULL);
	assert(pCommander != NULL);

	m_pCommander = pCommander;
}

void object_manager::SetUDP(network* pUdpNetwork) {
	assert(m_UdpNetwork == NULL);
	assert(pUdpNetwork != NULL);

	m_UdpNetwork = pUdpNetwork;
}

bool object_manager::AddClient(ip ClientIP, unsigned int ClientCode, unsigned int PortNumber) {	
	cs_auto cs(&m_CriticalSection);

	if(ClientCode != m_pClient->GetClientCode()) {
		m_UdpNetwork->AddMulticastGroup(ClientIP, htons(PortNumber));
		m_OtherClientInfo.push_back(object_manager::client_access_info(ClientCode, ClientIP, PortNumber));
	}
	
	return true;
}

bool object_manager::DeleteClient(const unsigned int& ClientCode) {
	
	EnterCriticalSection(&m_CriticalSection);
	std::vector<int> DeleteObjectList;	
	for(int i = 0; i < m_lObjects.size(); ++i) 
		if(m_lObjects[i].Owner == ClientCode)
			DeleteObjectList.push_back(m_lObjects[i].ID);
	LeaveCriticalSection(&m_CriticalSection);

	for(int i = 0; i < DeleteObjectList.size(); ++i) 
		this->DeleteObject(DeleteObjectList[i]);

	// delete multicasting.
	client_info_list::iterator it;
	for(it = m_OtherClientInfo.begin();
		it != m_OtherClientInfo.end();
		++it) 
			if(it->ClientCode == ClientCode)
				break;

	//assert(it != m_OtherClientInfo.end());
	if(it != m_OtherClientInfo.end()) {
		m_UdpNetwork->DeleteMulticastGroup(it->IP, htons(it->UdpPort));
		m_OtherClientInfo.erase(it);
	}

	return true;
}

bool object_manager::AddObject(const unsigned int& ObjectID, const OBJECT_TYPE& ObjectType, const unsigned int& OwnerCode, 
							   const int& ExtraLength, const char* Extra) {
	cs_auto cs(&m_CriticalSection);	

	if(ObjectType == OT_MISSILE || ObjectType == OT_BOOM ) {
		extra_3d_object* RealExtra = (extra_3d_object*)Extra;
		
		CScene::object_data Add;
		Add.Object				= new CGameObject;
		Add.ID					= ObjectID;
		Add.ObjectType			= ObjectType;
		Add.Owner				= OwnerCode;
		Add.Tick				= 0;
		Add.MakerTick			= GetTickCount();
		Add.SyncPositionVector	= RealExtra->Pos;
		Add.MoveVector			= D3DXVECTOR3(0.0f, 0.0f, 0.0f);			

		// object create. onec.		
		if(gs_MissileMesh == NULL) {
			gs_MissileMesh = new CCubeMesh;
			gs_MissileMesh->Build(this->m_pD3DDevice, this);			
			gs_MissileMesh->AddRef();
		}		
		if(gs_BoomMesh == NULL) {
			gs_BoomMesh = new CBoomMesh;
			gs_BoomMesh->Build(this->m_pD3DDevice, this);			
			gs_BoomMesh->AddRef();
		}

		if(ObjectType == OT_MISSILE)
			Add.Object->SetMesh(gs_MissileMesh);
		else
			Add.Object->SetMesh(gs_BoomMesh);

		// set object position. 
		D3DXMatrixRotationQuaternion(&(Add.Object->m_mtxWorld), &(RealExtra->Rotation));
		Add.Object->m_mtxWorld._41 = RealExtra->Pos.x;
		Add.Object->m_mtxWorld._42 = RealExtra->Pos.y;
		Add.Object->m_mtxWorld._43 = RealExtra->Pos.z;

		if(ObjectType == OT_MISSILE) {
			Add.MoveVector = D3DXVECTOR3(Add.Object->m_mtxWorld._31,
										 Add.Object->m_mtxWorld._32,
										 Add.Object->m_mtxWorld._33);
			Add.MoveVector *= MISSILE_SPEED;	// default missile speed. 
		}

		m_lObjects.push_back(Add);
	}
	
	else if(ObjectType == OT_SHIP) {
		if(OwnerCode == m_pClient->GetClientCode()) 
			m_ShipCode = ObjectID;
		else {
			extra_3d_object* RealExtra = (extra_3d_object*)Extra;

			CScene::object_data Add;
			Add.Object				= new CGameObject;
			Add.ID					= ObjectID;
			Add.ObjectType			= ObjectType;
			Add.Owner				= OwnerCode;
			Add.Tick				= 0;
			Add.SyncPositionVector	= RealExtra->Pos;
			Add.MakerTick			= GetTickCount();
			Add.Deleting            = false;
			Add.MoveVector			= D3DXVECTOR3(0.0f, 0.0f, 0.0f);			

			if(gs_ShipMesh == NULL) {		
				gs_ShipMesh = new CMesh;
				gs_ShipMesh->LoadFromXFile(_T("Data/sfb-04.X"), D3DXMESH_MANAGED, m_pD3DDevice, this); 								
				gs_ShipMesh->AddRef();
			}

			Add.Object->SetMesh(gs_ShipMesh);

			// set object position. 
			D3DXMatrixRotationQuaternion(&(Add.Object->m_mtxWorld), &(RealExtra->Rotation));
			Add.Object->m_mtxWorld._41 = RealExtra->Pos.x;
			Add.Object->m_mtxWorld._42 = RealExtra->Pos.y;
			Add.Object->m_mtxWorld._43 = RealExtra->Pos.z;

			m_lObjects.push_back(Add);
		}
	}

	return true;
}

bool object_manager::DeleteObject(const unsigned int& ObjectID) {
	cs_auto cs(&m_CriticalSection);

	std::vector<object_data>::iterator it;
	for(it = m_lObjects.begin(); it != m_lObjects.end(); ++it) 
		if(it->ID == ObjectID)
			break;
	
	if(it != m_lObjects.end()) {
		delete it->Object;
		m_lObjects.erase(it);
	}	
	return true;
}

bool object_manager::UpdateOtherClientObject(sync_object_3d* SyncData) {
	cs_auto cs(&m_CriticalSection);

	client_info_list::iterator it = m_OtherClientInfo.end();
	if(m_ShowRecvSyncDataCount) { // calculation total recv count		
		for(it = m_OtherClientInfo.begin();
			it != m_OtherClientInfo.end();
			++it) 
			if(it->ClientCode == SyncData->OwnerCode) {
				if(it->LastUpdateTick + 1000 < GetTickCount()) {// update 1 sec. 									
					it->LastEffectiveUdpRecvCountForFrame	= it->EffectiveUdpRecvCountForFrame;									
					it->LastUdpRecvCount					= it->UdpRecvCountForFrame;
					it->LastUpdateTick						= GetTickCount();									
					it->UdpRecvCountForFrame				= 0;			
					it->EffectiveUdpRecvCountForFrame		= 0;
				}
				++(it->UdpRecvCountForFrame);				
			break;						
		}
	}		


	bool FoundObject = false;	
	for(int i = 0; i < m_lObjects.size(); ++i) {
		if((m_lObjects[i].ID == SyncData->ObjectCode) && (m_lObjects[i].Owner != m_pClient->GetClientCode())) 
			if(m_lObjects[i].ObjectType == OT_MISSILE || m_lObjects[i].ObjectType == OT_SHIP) {
				assert(m_lObjects[i].Owner == SyncData->OwnerCode);		

				if(m_ShowRecvSyncDataCount && it != m_OtherClientInfo.end()) 
					if(m_lObjects[i].Tick < SyncData->SendTick)
						++(it->EffectiveUdpRecvCountForFrame);				

				
				if(m_lObjects[i].Tick < SyncData->SendTick) {
					// calculation Moving vector. 
					m_lObjects[i].MoveVector = SyncData->SyncData.Pos - m_lObjects[i].SyncPositionVector;					
					m_lObjects[i].MoveVector /= (float)(SyncData->SendTick - m_lObjects[i].Tick) * 0.001;

					// set new position. 
					D3DXMatrixRotationQuaternion(&(m_lObjects[i].Object->m_mtxWorld), &(SyncData->SyncData.Rotation));
					m_lObjects[i].Object->m_mtxWorld._41	= SyncData->SyncData.Pos.x;
					m_lObjects[i].Object->m_mtxWorld._42	= SyncData->SyncData.Pos.y;
					m_lObjects[i].Object->m_mtxWorld._43	= SyncData->SyncData.Pos.z;

					m_lObjects[i].SyncPositionVector	= SyncData->SyncData.Pos;
 					m_lObjects[i].Tick					= SyncData->SendTick;
					m_lObjects[i].MakerTick				= GetTickCount();
				}				
				FoundObject = true;
				
				break;
			}		
	}

	if(FoundObject)
		return true;
	else
		return false;
}

bool object_manager::UpdateObject(float ElapsedTime) {
	cs_auto cs(&m_CriticalSection);

	for(int i = 0; i < m_lObjects.size(); ++i) {
		if(m_lObjects[i].Owner == m_pClient->GetClientCode()) {
			if(m_lObjects[i].ObjectType == OT_BOOM && // Boom Object. 
				m_lObjects[i].Deleting != true) {
				if(m_lObjects[i].MakerTick + 1500 < GetTickCount()) {
					m_pServiceRequester->RequestNOTICE_DEL_OBJECT(m_lObjects[i].ID);
					m_lObjects[i].Deleting = true;
				}
			}			
			
			if(m_lObjects[i].ObjectType == OT_MISSILE && // Missile Object.
				m_lObjects[i].Deleting != true) {				
				if(m_lObjects[i].MakerTick + 12000 < GetTickCount()) {
					// time out. delete object.
					m_pServiceRequester->RequestNOTICE_DEL_OBJECT(m_lObjects[i].ID);
					m_lObjects[i].Deleting = true;
				}
				else {
					// check missile crush.
					float MoveLength = MISSILE_SPEED * ElapsedTime;
					float Near = 1000000.0f;
					int CrushShipID = -1;

					// check missile. by ray intersection 
					D3DXVECTOR3	RayPos(m_lObjects[i].Object->m_mtxWorld._41,
									   m_lObjects[i].Object->m_mtxWorld._42,
									   m_lObjects[i].Object->m_mtxWorld._43);
					D3DXVECTOR3	RayDir(m_lObjects[i].Object->m_mtxWorld._31,
									   m_lObjects[i].Object->m_mtxWorld._32,
									   m_lObjects[i].Object->m_mtxWorld._33);
					for(int Crush = 0; Crush < m_lObjects.size(); ++Crush)
					{							
						if((m_lObjects[Crush].ObjectType == OT_SHIP) && 
							m_lObjects[Crush].Owner !=  m_pClient->GetClientCode()) 
						{									
								if(m_lObjects[Crush].Object->m_pMesh->CheckRayIntersection(
									&(m_lObjects[Crush].Object->m_mtxWorld),
									RayPos, RayDir, MoveLength, &Near) == TRUE) 
								{
										if(Near <= MoveLength) 
										{
											CrushShipID = Crush;
											MoveLength = Near;
										}										
								}																			
						}													
					}

					// move missile
					m_lObjects[i].Object->MoveForward(MoveLength);

					if(CrushShipID != -1) 
					{	// notice crush ship infomation and boom object. 						
						m_pServiceRequester->RequestNOTICE_DEL_OBJECT(m_lObjects[i].ID);
						m_lObjects[i].Deleting = true;

						OBJECT_TYPE ObjectType(OT_BOOM);
						
						extra_3d_object Extra;
						Extra.Pos = RayPos;
						D3DXQuaternionIdentity(&(Extra.Rotation));
							
						m_pServiceRequester->RequestNOTICE_NEW_OBJECT(ObjectType, sizeof(extra_3d_object), (char*)&Extra); 
						m_pServiceRequester->RequestNOTICE_MISSILE_CRUSH(m_lObjects[i].ID, m_lObjects[CrushShipID].ID);
					}


					// send sync data. 
					sync_object_3d SendData;
					SendData.SyncType	= ST_OBJECT;
					SendData.OwnerCode  = m_pClient->GetClientCode();
					SendData.ObjectType = OT_MISSILE;
					SendData.ObjectCode = m_lObjects[i].ID;
					SendData.SendTick	= GetTickCount();
					SendData.SyncData.Pos = D3DXVECTOR3(m_lObjects[i].Object->m_mtxWorld._41,
														m_lObjects[i].Object->m_mtxWorld._42,
														m_lObjects[i].Object->m_mtxWorld._43);					
					D3DXMATRIX mat = m_lObjects[i].Object->m_mtxWorld;
					mat._41 = 0.0f; mat._42 = 0.0f; mat._43 = 0.0f;
					D3DXQuaternionRotationMatrix(&SendData.SyncData.Rotation, &mat);

					m_UdpNetwork->SendGroup((char*)&SendData, sizeof(SendData));
					++m_CurrentMySendSyncDataCount;					
				}
			}
		}
		else { // other client's objects. 
			if(m_PosInterpolation) 
				if(m_lObjects[i].ObjectType == OT_MISSILE || m_lObjects[i].ObjectType == OT_SHIP) {
					float Elap = ((GetTickCount() - m_lObjects[i].MakerTick) * 0.001f);
					if(ElapsedTime < ((GetTickCount() - m_lObjects[i].MakerTick) * 0.001f)) { // recv data is too old. 
						m_lObjects[i].Object->m_mtxWorld._41 += m_lObjects[i].MoveVector.x * ElapsedTime;
						m_lObjects[i].Object->m_mtxWorld._42 += m_lObjects[i].MoveVector.y * ElapsedTime;
						m_lObjects[i].Object->m_mtxWorld._43 += m_lObjects[i].MoveVector.z * ElapsedTime;

						m_lObjects[i].MakerTick = GetTickCount();
					}
				}
		}
	}

	// send my ship position.
	D3DXVECTOR3 vRight	= GetPlayer(0)->GetRightVector();
	D3DXVECTOR3 vUp		= GetPlayer(0)->GetUpVector();
	D3DXVECTOR3 vLook	= GetPlayer(0)->GetLookAtVector();

	D3DXMATRIX RotatoinMatrix;
	D3DXMatrixIdentity(&RotatoinMatrix);
	RotatoinMatrix._11 = vRight.x;	RotatoinMatrix._21 = vUp.x;		RotatoinMatrix._31 = vLook.x;
	RotatoinMatrix._12 = vRight.y;	RotatoinMatrix._22 = vUp.y;		RotatoinMatrix._32 = vLook.y;
	RotatoinMatrix._13 = vRight.z;	RotatoinMatrix._23 = vUp.z;		RotatoinMatrix._33 = vLook.z;

	sync_object_3d SendData;
	SendData.OwnerCode  = m_pClient->GetClientCode();
	SendData.SyncType = ST_OBJECT;
	SendData.ObjectType = OT_SHIP;
	SendData.ObjectCode = m_ShipCode;
	SendData.SendTick = GetTickCount();	
	SendData.SyncData.Pos = GetPlayer(0)->GetPosition();
	D3DXQuaternionRotationMatrix(&SendData.SyncData.Rotation, &RotatoinMatrix);	

	m_UdpNetwork->SendGroup((char*)&SendData, sizeof(SendData));
	++m_CurrentMySendSyncDataCount;

	// debug order. send count update. 
	if(m_LastUpdateMySendSyncDataTick + 1000 < GetTickCount()) {
		m_LastUpdateMySendSyncDataTick = GetTickCount();
		m_LastMySendSyncDataCount = m_CurrentMySendSyncDataCount;
		m_CurrentMySendSyncDataCount = 0;
	}
	return true;
}

bool object_manager::UpdateCommander(void) {	
	static unsigned int LastShowTick = 0;

	if(LastShowTick + 1000 < GetTickCount()) {
		LastShowTick = GetTickCount();

		if(m_ShowRecvSyncDataCount) 
			for(client_info_list::iterator it = m_OtherClientInfo.begin();
				it != m_OtherClientInfo.end();
				++it) 
				if(it->ClientCode != 0) // 0 is server
					if(m_ShowEffectiveRecvSyncDataCount)
						m_pCommander->Out(_T("[%d Client] Recv Udp Count : [%d][%d]"), 
									it->ClientCode, it->LastUdpRecvCount, it->LastEffectiveUdpRecvCountForFrame);	
					else
						m_pCommander->Out(_T("[%d Client] Recv Udp Count : [%d]"), 
									it->ClientCode, it->LastUdpRecvCount);		

		if(m_ShowMySendSyncDataCount)
			m_pCommander->Out(_T("[Send UDP Count] %d"), m_LastMySendSyncDataCount);	
	}

	return true;
}

bool object_manager::FireMissile(void) {
	static int BeforeTick = 0;

	if(GetTickCount() < BeforeTick + 1000)
		return false;

	BeforeTick = GetTickCount();

	OBJECT_TYPE ObjectType(OT_MISSILE);

	D3DXVECTOR3 MissilePosition = m_plPlayer[0]->GetPosition();
	D3DXVECTOR3 vUp = m_plPlayer[0]->GetUpVector();
	D3DXVECTOR3 vLook = m_plPlayer[0]->GetLookAtVector();
	D3DXVECTOR3 vRight = m_plPlayer[0]->GetRightVector();

	D3DXVec3Normalize(&vUp, &vUp);
	D3DXVec3Normalize(&vLook, &vLook);
	D3DXVec3Normalize(&vRight, &vRight);

	//MissilePosition += vLook * 150;

	D3DXMATRIX RotatoinMatrix;
	D3DXMatrixIdentity(&RotatoinMatrix);
	RotatoinMatrix._11 = vRight.x;	RotatoinMatrix._21 = vUp.x;		RotatoinMatrix._31 = vLook.x;
	RotatoinMatrix._12 = vRight.y;	RotatoinMatrix._22 = vUp.y;		RotatoinMatrix._32 = vLook.y;
	RotatoinMatrix._13 = vRight.z;	RotatoinMatrix._23 = vUp.z;		RotatoinMatrix._33 = vLook.z;

	extra_3d_object Extra;
	Extra.Pos = MissilePosition;
	D3DXQuaternionRotationMatrix(&Extra.Rotation, &RotatoinMatrix);	

 	m_pServiceRequester->RequestNOTICE_NEW_OBJECT(ObjectType, sizeof(extra_3d_object), (char*)&Extra); 

	// Missile Test Code.
	//static int Unit = 10;
	//this->AddObject(Unit++, ObjectType, 33/*m_pClient->GetClientCode()*/, sizeof(extra_3d_object), (char*)&Extra);
	// use temp client code. after delete. 

	return true;
}

//void object_manager::MotionUpdate(void) {
//	char	Buffer[MAX_SYNC_LENGTH];
//	int		RecvLen;
//
//	m_UdpNetwork->EventProcess();
//
//	if(m_UdpNetwork->CheckEvent(FD_READ)) {
//		if(m_UdpNetwork->RecvUDP(Buffer, sizeof(Buffer), &RecvLen, NULL, NULL) == false) 
//			return;			
//				
//		if(RecvLen == sizeof(sync_object_3d)) 
//			this->UpdateOtherClientObject((sync_object_3d*)Buffer);		
//	}
//}

void object_manager::ConnectOtherClients(void) {
	cs_auto cs(&m_CriticalSection);

	assert(1 <= m_OtherClientInfo.size()); // 0 is relay server.

	client_info_list::iterator it = m_OtherClientInfo.begin();
	++it;
	for(;
		it != m_OtherClientInfo.end(); 
		++it)
	 {		
		udp_link_thread_arg* Arg = new udp_link_thread_arg;		
		string IPS(inet_ntoa(it->IP));
		Arg->TargetIP			= IPS;
		Arg->TargetPort			= it->UdpPort;
		Arg->PtrUDPNetwork		= m_UdpNetwork;
		Arg->MyClientCode		= m_pClient->GetClientCode();
		m_pClient->ThreadManager().CreateNewThread(UdpLinkThread, Arg);
	}	
}

void object_manager::ConnectTargetClient(ip TargetIP, unsigned int UdpPort) {
	cs_auto cs(&m_CriticalSection);

	udp_link_thread_arg* Arg = new udp_link_thread_arg;	
	string IPS(inet_ntoa(TargetIP));
	Arg->TargetIP			= IPS;
	Arg->TargetPort			= UdpPort;
	Arg->PtrUDPNetwork		= m_UdpNetwork;
	Arg->MyClientCode		= m_pClient->GetClientCode();
	m_pClient->ThreadManager().CreateNewThread(UdpLinkThread, Arg);
}

void object_manager::TryUdpLink(unsigned int ClientCode) {
	cs_auto cs(&m_CriticalSection);
	
	for(client_info_list::iterator it = m_OtherClientInfo.begin();
		it != m_OtherClientInfo.end();
		++it) 
			if(it->ClientCode == ClientCode) 
			{
				++(it->UdpLinkCount);
				break;
			}	
}

bool object_manager::SwitchShowRecvSyncDataCount(void) { 
	m_ShowRecvSyncDataCount = !m_ShowRecvSyncDataCount; 
	return m_ShowRecvSyncDataCount;
}

bool object_manager::SwitchShowEffectiveRecvSyncDataCount(void) { 
	m_ShowEffectiveRecvSyncDataCount = !m_ShowEffectiveRecvSyncDataCount; 
	return m_ShowEffectiveRecvSyncDataCount;
}

bool object_manager::SwitchShowMySendSyncDataCount(void) { 
	m_ShowMySendSyncDataCount = !m_ShowMySendSyncDataCount; 
	return m_ShowMySendSyncDataCount;
}

bool object_manager::SwitchPosInterpolation(void) { 
	m_PosInterpolation = !m_PosInterpolation; 
	return m_PosInterpolation;
}

object_manager::client_info_list object_manager::GetClientList(void) { 
	cs_auto cs(&m_CriticalSection);

	return m_OtherClientInfo; 
}